package com.tpp.base.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tpp.base.elastic.ItemDto;
import com.tpp.base.elastic.ItemResult;
import com.tpp.base.elastic.ItemSource;
import com.tpp.base.feign.ImageFeign;
import com.tpp.base.mapper.ItemCommentMapper;
import com.tpp.base.mapper.ItemMapper;
import com.tpp.base.service.ItemService;
import com.tpp.base.vo.*;
import com.tpp.comm.pojo.TpImage;
import com.tpp.comm.pojo.TpItemComment;
import com.tpp.comm.util.TpConst;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ItemServiceImpl implements ItemService {

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Resource
    private ItemMapper itemMapper;

    @Resource
    private ImageFeign imageFeign;

    @Resource
    private ItemCommentMapper itemCommentMapper;

    @Override
    public List<FloorAndItems> queryFloorItems() {
        BoundHashOperations<String, FloorHashKey, List<ItemVo>> hashOps = redisTemplate.boundHashOps(TpConst.Redis.INDEX_FLOOR_KEY);
        Map<FloorHashKey, List<ItemVo>> hash = hashOps.entries();
        List<FloorAndItems> floorAndItemsList = new ArrayList<>();
        for (FloorHashKey floorHashKey : hash.keySet()) {
            List<ItemVo> itemVos = hash.get(floorHashKey).subList(0,5);
            FloorAndItems floorAndItems = new FloorAndItems();
            BeanUtils.copyProperties(floorHashKey,floorAndItems);
            List<FloorItemVo> floorItemVos = itemVos.stream().map(itemVo -> {
                FloorItemVo vo = new FloorItemVo();
                BeanUtils.copyProperties(itemVo, vo);
                return vo;
            }).toList();
            floorAndItems.setItems(floorItemVos);
            floorAndItemsList.add(floorAndItems);
        }
        return floorAndItemsList.stream().sorted((f1, f2) -> f1.getFloor() - f2.getFloor()).toList();
    }

    @Override
    public ItemDetailVo queryItemDetail(Integer itemId) {
        BoundHashOperations<String,FloorHashKey,List<ItemVo>> hashOps = redisTemplate.boundHashOps(TpConst.Redis.INDEX_FLOOR_KEY);
        List<ItemVo> itemVos = hashOps.values().stream().flatMap(List::stream).toList();
        Optional<ItemVo> first = itemVos.stream().filter(itemVo -> itemVo.getId().equals(itemId)).findFirst();
        ItemDetailVo itemDetailVo=null;
        if(!first.isEmpty()){
            itemDetailVo = first.map(itemVo -> {
                ItemDetailVo vo = new ItemDetailVo();
                BeanUtils.copyProperties(itemVo, vo);
                return vo;
            }).get();
        }
        return itemDetailVo;
    }

    @Override
    public ItemPager queryItemPager(ItemDto itemDto) {

        BoolQueryBuilder bool = new BoolQueryBuilder();
        bool.must(new MatchQueryBuilder("itemName",itemDto.getKeyword()));
        bool.should(new MatchQueryBuilder("abstractMessage",itemDto.getKeyword()));

        if(itemDto.getAreaId()!=0){
            bool.must(new TermQueryBuilder("areaId",itemDto.getAreaId()));
        }

        if(itemDto.getItemTypeId1()!=0){
            bool.must(new TermQueryBuilder("itemTypeId1",itemDto.getItemTypeId1()));
        }

        if(itemDto.getItemTypeId2()!=0){
            bool.must(new TermQueryBuilder("itemTypeId2",itemDto.getItemTypeId2()));
        }

        if(itemDto.getTimeind()!=0){
            bool.must(new TermQueryBuilder("startTime", LocalDate.now().plusDays(itemDto.getTimeind()-1)));
        }

        if(itemDto.getStartTime()!=null){
            bool.must(new RangeQueryBuilder("startTime").gte(itemDto.getStartTime()));
        }

        if(itemDto.getEndTime()!=null){
            bool.must(new RangeQueryBuilder("startTime").lte(itemDto.getEndTime()));
        }

        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder
                .preTags("<em style='color:red;'>")
                .field("itemName")
                .field("abstractMessage")
                .postTags("</em>");

        Sort sort = null;//sort为null时，默认按照相关度排序
        switch (itemDto.getSort()){
            case 1:
                sort = Sort.by(Sort.Order.desc("startTime"));
                break;
            case 2:
                sort = Sort.by(Sort.Order.asc("createTime"));
        }

        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder
                .withQuery(bool)
                .withHighlightBuilder(highlightBuilder)
                .withSort(sort);
        Pageable pageable = PageRequest.of(itemDto.getCurrentPage()-1,itemDto.getPageSize());
        nativeSearchQueryBuilder.withPageable(pageable);
        SearchHits<ItemSource> hits = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), ItemSource.class);

        List<ItemResult> rows = hits.stream().map(hit -> {
            ItemResult itemResult = new ItemResult();
            ItemSource itemSource = hit.getContent();
            BeanUtils.copyProperties(itemSource, itemResult);
            itemResult.setStartTime(itemSource.getStartTime().toLocalDate());
            itemResult.setEndTime(itemSource.getEndTime().toLocalDate());
            List<String> itemNameList = hit.getHighlightField("itemName");
            if (itemNameList.size() > 0) {
                String itemName = String.join(" ", itemNameList);
                itemResult.setItemName(itemName);
            }
            List<String> abstractMessageList = hit.getHighlightField("abstractMessage");
            if (abstractMessageList.size() > 0) {
                String abstractMessage = String.join(" ", abstractMessageList);
                itemResult.setAbstractMessage(abstractMessage);
            }
            return itemResult;
        }).collect(Collectors.toList());

        ItemPager itemPager = new ItemPager();
        itemPager.setRows(rows);
        itemPager.setTotal(hits.getTotalHits());
        itemPager.setCurrentPage(itemDto.getCurrentPage());
        itemPager.setPageSize(itemDto.getPageSize());
        if(hits.getTotalHits()%itemDto.getPageSize()!=0){
            itemPager.setPageCount(hits.getTotalHits()/itemDto.getPageSize()+1);
        }else{
            itemPager.setPageCount(hits.getTotalHits()/itemDto.getPageSize());
        }
        return itemPager;
    }

    @Override
    public List<ItemRecommendVo> queryTodayRecommend() {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.withQuery(new MatchQueryBuilder("isRecommend",1));
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.fragmentSize(20);
        builder.withHighlightBuilder(highlightBuilder);
        builder.withSort(Sort.by(Sort.Order.desc("startTime")));
        NativeSearchQuery build = builder.build();

        SearchHits<ItemSource> search = elasticsearchRestTemplate.search(build, ItemSource.class);

        List<ItemRecommendVo> collect = search.stream().map(e -> {
            ItemSource content = e.getContent();
            ItemRecommendVo todayRecommend = new ItemRecommendVo();
            todayRecommend.setId(content.getId());
            todayRecommend.setMinPrice(content.getMinPrice());
            todayRecommend.setItemName(content.getItemName());
            todayRecommend.setImgUrl(content.getImgUrl());
            todayRecommend.setStartTime(content.getStartTime());
            return todayRecommend;
        }).collect(Collectors.toList());
        return collect;
    }

    @Override
    public List<ItemRecommendVo> queryToSaleItem() {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.fragmentSize(20);
        builder.withHighlightBuilder(highlightBuilder);
        builder.withSort(Sort.by(Sort.Order.desc("startTime")));
        NativeSearchQuery build = builder.build();

        SearchHits<ItemSource> search = elasticsearchRestTemplate.search(build, ItemSource.class);

        List<ItemRecommendVo> collect = search.stream().map(e -> {
            ItemSource content = e.getContent();
            ItemRecommendVo todayRecommend = new ItemRecommendVo();
            todayRecommend.setId(content.getId());
            todayRecommend.setMinPrice(content.getMinPrice());
            todayRecommend.setItemName(content.getItemName());
            todayRecommend.setImgUrl(content.getImgUrl());
            todayRecommend.setStartTime(content.getStartTime());
            return todayRecommend;
        }).collect(Collectors.toList());
        return collect;
    }

    @Override
    public List<ItemRecommendVo> queryHotItems(Integer itemTypeId) {
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        searchQueryBuilder.withQuery(new MatchQueryBuilder("isRecommend",1));
        if(itemTypeId!=null&&itemTypeId!=0){
            searchQueryBuilder.withQuery(new MatchQueryBuilder("itemTypeId1",itemTypeId));
        }
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.fragmentSize(10);
        searchQueryBuilder.withHighlightBuilder(highlightBuilder);
        List<Sort.Order> orders = new ArrayList<>();
        orders.add(Sort.Order.desc("avgScore"));
        orders.add(Sort.Order.desc("commentCount"));
        searchQueryBuilder.withSort(Sort.by(orders));

        SearchHits<ItemSource> search = elasticsearchRestTemplate.search(searchQueryBuilder.build(), ItemSource.class);
        List<ItemRecommendVo> collect = search.stream().map(e -> {
            ItemSource content = e.getContent();
            ItemRecommendVo todayRecommend = new ItemRecommendVo();
            todayRecommend.setId(content.getId());
            todayRecommend.setMinPrice(content.getMinPrice());
            todayRecommend.setItemName(content.getItemName());
            todayRecommend.setImgUrl(content.getImgUrl());
            todayRecommend.setStartTime(content.getStartTime());
            return todayRecommend;
        }).collect(Collectors.toList());
        return collect;
    }

    @Override
    public List<ItemRecommendVo> queryItemRecommend(Integer itemTypeId, Integer limit) {
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        searchQueryBuilder.withQuery(new MatchQueryBuilder("isRecommend",1));
        if(itemTypeId!=null&&itemTypeId!=0){
            searchQueryBuilder.withQuery(new MatchQueryBuilder("itemTypeId1",itemTypeId));
        }
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        if(limit!=null&&limit!=0)
            highlightBuilder.fragmentSize(limit);
        else
            highlightBuilder.fragmentSize(10);
        searchQueryBuilder.withHighlightBuilder(highlightBuilder);
        List<Sort.Order> orders = new ArrayList<>();
        orders.add(Sort.Order.desc("avgScore"));
        orders.add(Sort.Order.desc("commentCount"));
        searchQueryBuilder.withSort(Sort.by(orders));

        SearchHits<ItemSource> search = elasticsearchRestTemplate.search(searchQueryBuilder.build(), ItemSource.class);
        List<ItemRecommendVo> collect = search.stream().map(e -> {
            ItemSource content = e.getContent();
            ItemRecommendVo todayRecommend = new ItemRecommendVo();
            todayRecommend.setId(content.getId());
            todayRecommend.setMinPrice(content.getMinPrice());
            todayRecommend.setItemName(content.getItemName());
            todayRecommend.setImgUrl(content.getImgUrl());
            todayRecommend.setStartTime(content.getStartTime());
            return todayRecommend;
        }).collect(Collectors.toList());
        return collect;
    }

    @Override
    public ItemCommentPage queryItemComment(ItemCommentPage itemCommentPage) {
        Page<TpItemComment> tpItemCommentPage = itemCommentMapper.selectPage(Page.of(itemCommentPage.getCurrentPage(), itemCommentPage.getPageSize()), new QueryWrapper<TpItemComment>()
                .lambda()
                .eq(TpItemComment::getItemId, itemCommentPage.getId())
                .orderByDesc(TpItemComment::getCreatedTime)
        );
        itemCommentPage.setTotal(tpItemCommentPage.getTotal());
        List<ItemCommentVo> collect = tpItemCommentPage.getRecords().stream().map(e -> {
            ItemCommentVo itemCommentVo = new ItemCommentVo();
            BeanUtils.copyProperties(e, itemCommentVo);
            TpImage image = imageFeign.findImage(e.getId(), 1, 0);
            itemCommentVo.setImgUrl(image.getImgUrl());
            return itemCommentVo;
        }).collect(Collectors.toList());
        itemCommentPage.setItemComments(collect);
        return itemCommentPage;
    }

    @Override
    public void commitItemComment(AddCommentVo addCommentVo) {
        TpItemComment tpItemComment = TpItemComment.builder()
                .itemId(addCommentVo.getItemId())
                .userId(addCommentVo.getUserId())
                .content(addCommentVo.getComment())
                .score(addCommentVo.getScore())
                .createdTime(LocalDateTime.now()).build();
        itemCommentMapper.insert(tpItemComment);
    }
}
